//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "fSynthTerrainC.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma link "GLS.BaseClasses"
#pragma link "GLS.Cadencer"
#pragma link "GLS.Coordinates"

#pragma link "GLS.HeightData"
#pragma link "GLS.Material"
#pragma link "GLS.Objects"
#pragma link "GLS.Scene"
#pragma link "GLS.TerrainRenderer"
#pragma link "GLS.SceneViewer"

#pragma link "GLS.HeightData"
#pragma link "GLS.HeightData"
#pragma resource "*.dfm"
TForm1* Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) {}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject* Sender)
{
    int i;
    TBitmap* bmp;

    // 8 MB height data cache
    // Note this is the data size in terms of elevation samples, it does not
    // take into account all the data required/allocated by the renderer
    GLCustomHDS->MaxPoolSize = 8 * 1024 * 1024;
    // Move camera starting point to an interesting hand-picked location
    DummyCube1->Position->X = 50;
    DummyCube1->Position->Z = 150;
    // Initial camera height offset (controled with pageUp/pageDown)
    FCamHeight = 20;
    // We build several basic 1D textures which are just color ramps
    // all use automatic texture mapping corodinates, in ObjectLinear method
    // (ie. texture coordinates for a vertex depend on that vertex coordinates)
    bmp = new TBitmap;
    bmp->PixelFormat = pf24bit;
    bmp->Width = 256;
    bmp->Height = 1;
    // Black-White ramp, autotexture maps to Z coordinate
    // This one changes with altitude, this is a quick way to obtain
    // altitude-dependant coloring
    for (i = 0; i < 255; i++)
        bmp->Canvas->Pixels[i][0] = RGB(i, i, i);
    GLMaterialLibrary1->AddTextureMaterial("BW", bmp)
        ->Material->Texture->MappingMode = tmmObjectLinear;
    GLMaterialLibrary1->AddTextureMaterial("BW", bmp)
        ->Material->Texture->MappingSCoordinates->AsVector =
        VectorMake(0, 0, 0.0001, 0);
    // Red, Blue map linearly to X and Y axis respectively
    for (i = 0; i < 255; i++)
        bmp->Canvas->Pixels[i][0] = RGB(i, 0, 0);
    GLMaterialLibrary1->AddTextureMaterial("Red", bmp)
        ->Material->Texture->MappingMode = tmmObjectLinear;
    GLMaterialLibrary1->AddTextureMaterial("Red", bmp)
        ->Material->Texture->MappingSCoordinates->AsVector =
        VectorMake(0.1, 0, 0, 0);
    for (i = 0; i < 255; i++)
        bmp->Canvas->Pixels[i][0] = RGB(0, 0, i);

    GLMaterialLibrary1->AddTextureMaterial("Blue", bmp)
        ->Material->Texture->MappingMode = tmmObjectLinear;
    GLMaterialLibrary1->AddTextureMaterial("Blue", bmp)
        ->Material->Texture->MappingSCoordinates->AsVector =
        VectorMake(0, 0.1, 0, 0);
    bmp->Free();
    TerrainRenderer1->MaterialLibrary = GLMaterialLibrary1;
}

//---------------------------------------------------------------------------
//
// The beef : this event does all the interesting elevation data stuff
//
void __fastcall TForm1::GLCustomHDSStartPreparingData(TGLHeightData* HeightData)
{
	int x, y;
	TByteRaster rasterLine;
	TGLHeightDataType oldType;
//	TByteVector* b;
	int b;
	float d, dy;

 //	b = new (Byte);
	HeightData->DataState = hdsPreparing;
	// retrieve data
	oldType = HeightData->DataType;
	HeightData->Allocate(hdtByte);
	// Cheap texture changed (32 is our tileSize = 2^5)
	// This basicly picks a texture for each tile depending on the tile's position
	switch ((((HeightData->XLeft ^ HeightData->YTop) << 5) && 3)) {
		case 0, 3:
			HeightData->MaterialName = "BW";
			break;
		case 1:
			HeightData->MaterialName = "Blue";
			break;
		case 2:
			HeightData->MaterialName = "Red";
			break;
		default:;
	}
	// 'Cheap' elevation data : this is just a formula z=f(x, y)
	for (y = HeightData->YTop; y < HeightData->YTop + HeightData->Size - 1; y++)
	{
		rasterLine = HeightData->ByteRaster[y - HeightData->YTop];
		dy = y * y;
		for (x = HeightData->XLeft;
			 x < HeightData->XLeft + HeightData->Size - 1; x++) {
			d = sqrt(x * x + dy);
			b = RoundInt(128 + 128 * Sin(d * 0.2) / (d * 0.1 + 1));
			rasterLine[x - HeightData->XLeft] = b; /// ???
		}
	}
	if (oldType != hdtByte)
		HeightData->DataType = oldType;

}
//---------------------------------------------------------------------------
// Movement, mouse handling etc.

void __fastcall TForm1::GLSceneViewer1MouseDown(
    TObject* Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
{
    mx = X;
    my = Y;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::GLSceneViewer1MouseMove(
    TObject* Sender, TShiftState Shift, int X, int Y)
{
    if (Shift.Contains(ssLeft)) {
        GLCamera1->MoveAroundTarget(my - Y, mx - X);
        mx = X;
        my = Y;
    }
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Timer1Timer(TObject* Sender)
{
    Caption =
        "Synthetic Terrain " +
        Format("%.1f FPS - %d", ARRAYOFCONST((GLSceneViewer1->FramesPerSecond(),
									TerrainRenderer1->LastTriangleCount)));
    GLSceneViewer1->ResetPerformanceMonitor();
}

//---------------------------------------------------------------------------

void __fastcall TForm1::FormKeyPress(TObject* Sender, System::WideChar &Key)
{
    switch (Key) {
        case '+':
            if (GLCamera1->DepthOfView < 4000) {
                GLCamera1->DepthOfView = GLCamera1->DepthOfView * 1.2;
                GLSceneViewer1->Buffer->FogEnvironment->FogEnd =
                    GLSceneViewer1->Buffer->FogEnvironment->FogEnd * 1.2;
                GLSceneViewer1->Buffer->FogEnvironment->FogStart =
                    GLSceneViewer1->Buffer->FogEnvironment->FogStart * 1.2;
            }
            break;
        case '-':
            if (GLCamera1->DepthOfView > 300) {
                GLCamera1->DepthOfView = GLCamera1->DepthOfView / 1.2;
                GLSceneViewer1->Buffer->FogEnvironment->FogEnd =
                    GLSceneViewer1->Buffer->FogEnvironment->FogEnd / 1.2;
                GLSceneViewer1->Buffer->FogEnvironment->FogStart =
                    GLSceneViewer1->Buffer->FogEnvironment->FogStart / 1.2;
            }
            break;
        case '*':
            if (TerrainRenderer1->CLODPrecision > 5)
				TerrainRenderer1->CLODPrecision =
                    RoundInt(TerrainRenderer1->CLODPrecision * 0.8);
            break;
        case '/':
            if (TerrainRenderer1->CLODPrecision < 500)
                TerrainRenderer1->CLODPrecision =
                    RoundInt(TerrainRenderer1->CLODPrecision * 1.2);
            break;
        case '8':
            if (TerrainRenderer1->QualityDistance > 40)
                TerrainRenderer1->QualityDistance =
                    RoundInt(TerrainRenderer1->QualityDistance * 0.8);
            break;
        case '9':
            if (TerrainRenderer1->QualityDistance < 1000)
                TerrainRenderer1->QualityDistance =
                    RoundInt(TerrainRenderer1->QualityDistance * 1.2);
            break;
        default:;
    }

    Key = 0x0;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::GLCadencer1Progress(
    TObject* Sender, const double deltaTime, const double newTime)
{
    float speed;

    // handle keypresses
    if (IsKeyDown(VK_SHIFT))
        speed = 5 * deltaTime;
    else
        speed = deltaTime;

    if (IsKeyDown(VK_RIGHT))
        DummyCube1->Translate(
            GLCamera1->Position->Z * speed, 0, -GLCamera1->Position->X * speed);
    if (IsKeyDown(VK_LEFT))
        DummyCube1->Translate(
            -GLCamera1->Position->Z * speed, 0, GLCamera1->Position->X * speed);
    if (IsKeyDown(VK_UP))
        DummyCube1->Translate(-GLCamera1->Position->X * speed, 0,
            -GLCamera1->Position->Z * speed);
    if (IsKeyDown(VK_DOWN))
        DummyCube1->Translate(
            GLCamera1->Position->X * speed, 0, GLCamera1->Position->Z * speed);
    if (IsKeyDown(VK_PRIOR))
        FCamHeight = FCamHeight + 10 * speed;
    if (IsKeyDown(VK_NEXT))
        FCamHeight = FCamHeight - 10 * speed;
    if (IsKeyDown(VK_ESCAPE))
        Close();
    // don't drop through terrain!
    DummyCube1->Position->Y =
        TerrainRenderer1->InterpolatedHeight(DummyCube1->Position->AsVector) +
        FCamHeight;
}
//---------------------------------------------------------------------------

